using System;
using System.IO;
using System.Text;
using System.Web;
using System.Xml.Serialization;
using SpiderMaster.Net;

namespace SpiderMaster.AspNet.TradingSystem.BusinessLogicLayer
{
    /// <summary> Contains the implementation for the site settings.
    /// </summary>
    public class SiteSettings
    {
        /// <summary> Initializes a new instance of the <see cref="SiteSettings"/> class.
        /// </summary>
        public SiteSettings() { }

        private static SiteSettings Instance;

        private const string XmlConfigFile = "~/App_Data/site-config.xml";

        private int _maxPhotosPerAd;
        private bool _storePhotosInDatabase;
        private string _serverPhotoUploadDirectory;

        private bool _adApprovalRequired;
        private bool _AdRequiresPayPalPayment;

        private bool _allowUsersToEditAds;
        private bool _allowUsersToDeleteAdsInDB;

        private int _maxAdRunningDays;
        private AdminNotificationSetting _adminNotification;

        private string _siteName;
        private string _siteEmail;

        /// <summary> Gets a value indicating whether [allow image uploads].
        /// </summary>
        /// <value><c>true</c> if [allow image uploads]; otherwise, <c>false</c>.</value>
        public bool AllowImageUploads { get { return (_maxPhotosPerAd > 0); } }

        /// <summary> Gets or sets the max photos per ad.
        /// </summary>
        /// <value>The max photos per ad.</value>
        public int MaxPhotosPerAd
        {
            get { return _maxPhotosPerAd; }
            set
            {
                lock (this)
                {
                    _maxPhotosPerAd = value;
                }
            }
        }

        /// <summary> Gets or sets a value indicating whether [store photos in database].
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [store photos in database]; otherwise, <c>false</c>.
        /// </value>
        public bool StorePhotosInDatabase
        {
            get { return _storePhotosInDatabase; }
            set
            {
                lock (this)
                {
                    _storePhotosInDatabase = value;
                }
            }
        }

        /// <summary> Gets or sets the server photo upload directory.
        /// </summary>
        /// <value>The server photo upload directory.</value>
        public string ServerPhotoUploadDirectory
        {
            get { return _serverPhotoUploadDirectory; }
            set
            {
                lock (this)
                {
                    _serverPhotoUploadDirectory = value;
                }
            }
        }

        /// <summary> Gets or sets a value indicating whether [ad activation required].
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [ad activation required]; otherwise, <c>false</c>.
        /// </value>
        public bool AdActivationRequired
        {
            get { return _adApprovalRequired; }
            set
            {
                lock (this)
                {
                    _adApprovalRequired = value;
                }
            }
        }

        /// <summary> Gets or sets a value indicating whether [ad paypal payment required].
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [ad activation required]; otherwise, <c>false</c>.
        /// </value>
        public bool AdPayPalPaymentRequired
        {
            get { return _AdRequiresPayPalPayment; }
            set
            {
                lock (this)
                {
                    _AdRequiresPayPalPayment = value;
                }
            }
        }


        /// <summary> Gets or sets a value indicating whether [allow users to edit ads].
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [allow users to edit ads]; otherwise, <c>false</c>.
        /// </value>
        public bool AllowUsersToEditAds
        {
            get { return _allowUsersToEditAds; }
            set
            {
                lock (this)
                {
                    _allowUsersToEditAds = value;
                }
            }
        }

        /// <summary> Gets or sets a value indicating whether [allow users to delete ads in DB].
        /// </summary>
        /// <value>
        /// 	<c>true</c> if [allow users to delete ads in DB]; otherwise, <c>false</c>.
        /// </value>
        public bool AllowUsersToDeleteAdsInDB
        {
            get { return _allowUsersToDeleteAdsInDB; }
            set
            {
                lock (this)
                {
                    _allowUsersToDeleteAdsInDB = value;
                }
            }
        }

        /// <summary> Gets or sets the max ad running days.
        /// </summary>
        /// <value>The max ad running days.</value>
        public int MaxAdRunningDays
        {
            get { return _maxAdRunningDays; }
            set
            {
                lock (this)
                {
                    _maxAdRunningDays = value;
                }
            }
        }

        /// <summary> Gets or sets the admin notification.
        /// </summary>
        /// <value>The admin notification.</value>
        [System.ComponentModel.TypeConverter(typeof(System.ComponentModel.EnumConverter))]
        public AdminNotificationSetting AdminNotification
        {
            get { return _adminNotification; }
            set
            {
                lock (this)
                {
                    _adminNotification = value;
                }
            }
        }

        /// <summary> Gets or sets the name of the site.
        /// </summary>
        /// <value>The name of the site.</value>
        public string SiteName
        {
            get { return _siteName; }
            set
            {
                lock (this)
                {
                    _siteName = value;
                }
            }
        }

        /// <summary> Gets or sets the site email address.
        /// </summary>
        /// <value>The site email address.</value>
        // TODO: Make Obsolete or internal.
        public string SiteEmailAddress
        {
            get { return _siteEmail; }
            set
            {
                lock (this)
                {
                    _siteEmail = value;
                }
            }
        }

        /// <summary> Gets the site email from field.
        /// </summary>
        /// <value>The site email from field.</value>
        public string MailSenderAddressFormatted { get { return String.Format("{0} <{1}>", _siteName, _siteEmail); } }

        /// <summary> Loads from configuration.
        /// </summary>
        /// <returns></returns>
        public static SiteSettings LoadFromConfiguration()
        {
            SiteSettings s = LoadFromXml();

            if (s == null)
            {
                s = new SiteSettings();
                s.MaxPhotosPerAd = 5;
                s.StorePhotosInDatabase = true;
                s.ServerPhotoUploadDirectory = "Upload";
                s.AdActivationRequired = false;
                s.AdPayPalPaymentRequired = false;
                s.AllowUsersToEditAds = true;
                s.AllowUsersToDeleteAdsInDB = true;
                s.MaxAdRunningDays = 21;
                s.AdminNotification = AdminNotificationSetting.None;
                s.SiteName = Product.Current.Name;
                s.SiteEmailAddress = "TradingCenter@yoursite.com";
                SaveToXml(s);
            }
            return s;
        }

        /// <summary> Gets the current Application Settings.
        /// </summary>
        /// <value>The current Application Settings.</value>
        public static SiteSettings Current { get { return GetSharedSettings(); } }

        /// <summary> Gets the instance.
        /// </summary>
        /// <returns></returns>
        public static SiteSettings GetSharedSettings()
        {
            if (Instance == null)
                Instance = SiteSettings.LoadFromConfiguration();

            return Instance;
        }

        /// <summary> Updates the settings.
        /// </summary>
        /// <param name="newSettings">The new settings.</param>
        /// <returns></returns>
        public static bool UpdateSettings(SiteSettings newSettings)
        {
            // write settings to code or db

            // update Application-wide settings, only over-writing settings that users should edit
            lock (Instance)
            {
                // Ads must be activated before appearing on the site
                Instance.AdActivationRequired = newSettings.AdActivationRequired;
                Instance.AdPayPalPaymentRequired = newSettings.AdPayPalPaymentRequired;

                // Store Photos in Database
                Instance.StorePhotosInDatabase = newSettings.StorePhotosInDatabase;
                // ... else: use the following directory to save uploaded Photos:
                Instance.ServerPhotoUploadDirectory = newSettings.ServerPhotoUploadDirectory;


                // Maximum Number of Photos to Upload
                Instance.MaxPhotosPerAd = newSettings.MaxPhotosPerAd;

                // Maximum Number of Days for which an Ad is active
                Instance.MaxAdRunningDays = newSettings.MaxAdRunningDays;

                // Allow Users to edit their own Ads
                Instance.AllowUsersToEditAds = newSettings.AllowUsersToEditAds;

                // Users to delete ads direclty in the database
                Instance.AllowUsersToDeleteAdsInDB = newSettings.AllowUsersToDeleteAdsInDB;

                // Notifications to Administrators
                Instance.AdminNotification = newSettings.AdminNotification;

                // Site Name
                Instance.SiteName = newSettings.SiteName;

                // Contact Email Address for Site
                Instance.SiteEmailAddress = newSettings.SiteEmailAddress;

                // Serialize to Xml Config File
                return SaveToXml(Instance);
            }
        }

        /// <summary> Loads from XML.
        /// </summary>
        /// <returns></returns>
        private static SiteSettings LoadFromXml()
        {
            SiteSettings settings = null;

            HttpContext context = HttpContext.Current;
            if (context != null)
            {
                string configPath = context.Server.MapPath(XmlConfigFile);

                XmlSerializer xml = null;
                FileStream fs = null;

                bool success = false;
                int numAttempts = 0;

                while (!success && numAttempts < 2)
                {
                    try
                    {
                        numAttempts++;
                        xml = new XmlSerializer(typeof(SiteSettings));
                        fs = new FileStream(configPath, System.IO.FileMode.Open, System.IO.FileAccess.Read);
                        settings = xml.Deserialize(fs) as SiteSettings;
                        success = true;
                    }
                    catch (Exception x)
                    {
                        // if an exception is thrown, there might have been a sharing violation;
                        // we wait and try again (max: two attempts)
                        success = false;
                        System.Threading.Thread.Sleep(1000);
                        if (numAttempts == 2)
                            throw new Exception("The Site Configuration could not be loaded.", x);
                    }
                }

                if (fs != null)
                    fs.Close();
            }

            return settings;
        }

        /// <summary> Gets the XML.
        /// </summary>
        /// <returns></returns>
        public string GetXml()
        {
            StringBuilder result = new StringBuilder();
            StringWriter s = new StringWriter(result);

            try
            {
                XmlSerializer xml = new XmlSerializer(typeof(SiteSettings));
                xml.Serialize(s, this);
            }
            finally { s.Close(); }

            return result.ToString();
        }

        /// <summary> Saves to XML.
        /// </summary>
        /// <param name="settings">The settings.</param>
        /// <returns></returns>
        private static bool SaveToXml(SiteSettings settings)
        {
            if (settings == null)
                return false;

            HttpContext context = HttpContext.Current;
            if (context == null)
                return false;

            string configPath = context.Server.MapPath(XmlConfigFile);

            XmlSerializer xml = null;
            System.IO.FileStream fs = null;

            bool success = false;
            int numAttempts = 0;

            while (!success && numAttempts < 2)
            {
                try
                {
                    numAttempts++;
                    xml = new XmlSerializer(typeof(SiteSettings));
                    fs = new FileStream(configPath, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
                    xml.Serialize(fs, settings);
                    success = true;
                }
                catch
                {
                    // if an exception is thrown, there might have been a sharing violation;
                    // we wait and try again (max: two attempts)
                    success = false;
                    System.Threading.Thread.Sleep(1000);
                }
            }

            if (fs != null)
                fs.Close();

            return success;
        }
    }
}

